// SPDX-License-Identifier: GPL-2.0
/******************************************************************************
 *    NAND Flash Controller nandbase Device Driver
 *    Copyright (c) 2009-2010 by Hisilicon.
 *    All rights reserved.
 * ***
 *
 ******************************************************************************/

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <asm/errno.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>

#include "hibase.h"
#include "mtdcore.h"

static const char * const fmt[] = {"%u", "%uK", "%uM", "%uG", "%uT", "%uT"};

uint8_t hi_read_byte(struct nand_chip *chip)
{
	struct hinfc_host *host = chip->priv;

	if (host->command == NAND_CMD_STATUS)
		return readb(chip->legacy.IO_ADDR_R);

	host->offset++;

	if (host->command == NAND_CMD_READID)
		return readb(chip->legacy.IO_ADDR_R + host->offset - 1);

	return readb(host->buffer + host->column + host->offset - 1);
}

u16 hi_read_word(struct nand_chip *chip)
{
	struct hinfc_host *host = chip->priv;

	host->offset += 2;
	return readw(host->buffer + host->column + host->offset - 2);
}

void hi_write_buf(struct nand_chip *chip, const uint8_t *buf, int len)
{
	struct hinfc_host *host = chip->priv;

	memcpy(host->buffer + host->column + host->offset, buf, len);
	host->offset += len;

	/* grand total the number of nand flash writing */
	g_nand_ewetimes.ui_writetimes++;
}

char *int_to_size(unsigned long long size)
{
	int i;
	static char buffer[20];

	for (i = 0; (i < 5) && !(size & 0x3FF) && size; i++)
		size = (size >> 10);
	snprintf(buffer, sizeof(buffer), fmt[i], size);
	return buffer;
}

void hi_reg_status_check(struct hinfc_host *host, unsigned int reg)
{
	unsigned int loop = 0;

	while (hinfc_read(host, reg) & HI_NAND_STATUS_START) {
		if (loop++ > HI_NAND_CHECK_STATUS_LOOP_MAX) {
			pr_warn("HI_NAND: controller operation time out, CS%d!\n",
				host->chipselect);
			return;
		}
		_cond_resched();
	}
}

int hi_reg_status_check_mdelay(struct hinfc_host *host, unsigned int reg)
{
	unsigned int loop = 0;

	for (loop = 0; loop < HI_NAND_CHIP_RESET_DELAY; loop++) {
		if ((hinfc_read(host, reg) & HI_NAND_STATUS_START))
			return 0;
		mdelay(1);
	}
	return -EIO;
}

void hinand_of_node_parse(struct platform_device *dev, const char *of_name)
{
	struct device_node *node = NULL;
	u32 reg_base[2] = {0};
	u32 mem_base[2] = {0};

	node = of_find_compatible_node(NULL, NULL, of_name);
	if (node == NULL)
		return;

	if (of_property_read_u32_array(node, "reg_base", reg_base, 2))
		goto ERROR;

	if (of_property_read_u32_array(node, "mem_base", mem_base, 2))
		goto ERROR;

	dev->resource[0].start = reg_base[0];
	dev->resource[0].end = reg_base[0] + reg_base[1];
	dev->resource[1].start = mem_base[0];
	dev->resource[1].end = mem_base[0] + mem_base[1];
	return;
ERROR:
	pr_info("Can not find fmc reg_base or mem_base in dtb, fmc will use default base addr");
}

MODULE_IMPORT_NS(HW_RTOS_NS);
